// Created by plusminus on 23:18:23 - 02.10.2008
package org.andnav.osm.views.overlay;

import java.util.ArrayList;
import java.util.List;

//import org.andnav.osm.R;
import com.robert.maps.R;

import org.andnav.osm.util.GeoPoint;
import org.andnav.osm.views.OpenStreetMapView;
import org.andnav.osm.views.OpenStreetMapView.OpenStreetMapViewProjection;

import android.app.AlertDialog;
import android.content.Context;
import android.content.res.Resources.NotFoundException;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Paint.Style;
import android.graphics.drawable.Drawable;
import android.view.MotionEvent;
import android.widget.Toast;

/**
 * Draws a list of {@link OpenStreetMapViewOverlayItem} as markers to a map.
 * The item with the lowest index is drawn as last and therefore the 'topmost' marker. It also gets checked for onTap first.    
 * This class is generic, because you then you get your custom item-class passed back in onTap(). 
 * @author Nicolas Gramlich
 *
 * @param <T>
 */
public class OpenStreetMapViewItemizedOverlay<T extends OpenStreetMapViewOverlayItem> extends OpenStreetMapViewOverlay {
	// ===========================================================
	// Constants
	// ===========================================================
	
	//protected static final Point DEFAULTMARKER_HOTSPOT = new Point(13, 47);
	protected static final Point DEFAULTMARKER_HOTSPOT = new Point(17, 47);

	// ===========================================================
	// Fields
	// ===========================================================
	
	protected OnItemTapListener<T> mOnItemTapListener;
	//joeng
	protected OnItemLongPressListener<T> mOnItemLongPressListener;
	protected final List<T> mItemList;
	protected final Point mMarkerHotSpot; 
	//protected final Drawable mMarker;
	protected Drawable mMarker;
	protected Paint mPathPaint = new Paint();
	protected final Context CTX;
	
	protected Path GeoPath = new Path();
	protected ArrayList<Integer> mPathPosList;
	
	protected final int mMarkerWidth, mMarkerHeight; 
	
	
	// ===========================================================
	// Constructors
	// ===========================================================

	public OpenStreetMapViewItemizedOverlay(final Context ctx, final List<T> aList, final OnItemTapListener<T> aOnItemTapListener) {		
        this(ctx, aList, null, null,aOnItemTapListener, null);
	}
	//joeng
	public OpenStreetMapViewItemizedOverlay(final Context ctx, final List<T> aList, final Drawable pMarker, final Point pMarkerHotspot, final OnItemTapListener<T> aOnItemTapListener) {
		this(ctx, aList, pMarker, pMarkerHotspot,aOnItemTapListener, null);
	}
	//joeng
	public OpenStreetMapViewItemizedOverlay(final Context ctx, final List<T> aList, final Drawable pMarker, 
			final Point pMarkerHotspot, final OnItemTapListener<T> aOnItemTapListener,
			final OnItemLongPressListener<T> aOnItemLongPressListener) {		
		assert(ctx != null);
		assert(aList != null);
		
		this.CTX = ctx;
		this.mMarker = (pMarker != null) ? pMarker : ctx.getResources().getDrawable(R.drawable.marker_default);
				
		this.mMarkerHotSpot = (pMarkerHotspot != null) ? pMarkerHotspot : DEFAULTMARKER_HOTSPOT;
		
        this.mOnItemTapListener = aOnItemTapListener;
        //joeng
        this.mOnItemLongPressListener = aOnItemLongPressListener;
        
		this.mMarkerWidth = this.mMarker.getIntrinsicWidth();
		this.mMarkerHeight = this.mMarker.getIntrinsicHeight();

		// Add one sample item.
		this.mItemList = aList;
	}

	// ===========================================================
	// Getter & Setter
	// ===========================================================

	// ===========================================================
	// Methods from SuperClass/Interfaces
	// ===========================================================
	
	@Override
	protected void onDrawFinished(Canvas c, OpenStreetMapView osmv) {
		return;
	}
	
	@Override
	public void onDraw(final Canvas c, final OpenStreetMapView mapView) {
		final OpenStreetMapViewProjection pj = mapView.getProjection();
		
		final Point curScreenCoords = new Point();
		final int viewWidth_2 = mapView.getWidth() / 2;
		final int viewHeight_2 = mapView.getHeight() / 2;
		//final double maxRadius = Math.sqrt((Math.pow(viewWidth_2 , 2) + Math.pow(viewHeight_2, 2)));
		GeoPath.reset();
		// draw path first
		if (mPathPosList != null){
			for(int i = 0; i< this.mPathPosList.size(); i ++){
				drawPath(c, pj, mItemList.get(mPathPosList.get(i)));
			}
		}
		
		/* Draw in backward cycle, so the items with the least index are on the front. */
		for(int i = this.mItemList.size() - 1; i >= 0; i--){
			T item = this.mItemList.get(i); 
//			if (item.mGeoPointList != null && item.mGeoPointList.size() > 1)
//				drawPath(c, pj, item);
//			else
			if (item.mGeoPointList != null && item.mGeoPointList.size() == 1){
				pj.toPixels(item.mGeoPointList.get(0), curScreenCoords);
				//only works on items that within visible area
//				if (mapView.getBearing()==0 && (curScreenCoords.x >=0 && curScreenCoords.y >=0)){
//					setMarkerIcon(item.mCategory);	
//					onDrawItem(c, i, curScreenCoords);
//				}
//				else{
//					final double radius = Math.sqrt((Math.pow(Math.abs(curScreenCoords.x-viewWidth_2) , 2) + 
//							Math.pow(Math.abs(curScreenCoords.y-viewHeight_2), 2)));
//					if (radius <= maxRadius){
						c.save();
						setMarkerIcon(item.mCategory);	
						c.rotate(mapView.getBearing(), curScreenCoords.x, curScreenCoords.y);
						onDrawItem(c, i, curScreenCoords);
						c.restore();
//					}
//				}
			}
		}		
	}
	/**
	 * @param c
	 * @param pj
	 */
	private void drawPath(final Canvas c, final OpenStreetMapViewProjection pj, final T item) {
		mPathPaint.setStyle(Style.STROKE);
		//tmpPaint.setARGB(255, 135, 133, 242);
		mPathPaint.setARGB(255, 255, 140, 25);
		mPathPaint.setAntiAlias(true);
		mPathPaint.setStrokeWidth((float) 3.5);
//		List<GeoPoint> PList = new ArrayList<GeoPoint>() ;
//		PList.add(new GeoPoint(22296125,114017189)); 
//		PList.add(new GeoPoint(22295370,114017273)); 
		c.drawPath(pj.toPixels(item.mGeoPointList , GeoPath),mPathPaint );
	}
	
	/**
	 * @param item
	 */
	private void setMarkerIcon(String CategoryName) {
		if (CategoryName.equalsIgnoreCase("Tourism")) 
			this.mMarker = CTX.getResources().getDrawable(R.drawable.marker1);
		else if (CategoryName.equalsIgnoreCase("Entertainment"))
			this.mMarker = CTX.getResources().getDrawable(R.drawable.marker2);
		else if (CategoryName.equalsIgnoreCase("Restaurant"))
			this.mMarker = CTX.getResources().getDrawable(R.drawable.marker3);
		else if (CategoryName.equalsIgnoreCase("Hotel"))
			this.mMarker = CTX.getResources().getDrawable(R.drawable.marker4);
		else if (CategoryName.equalsIgnoreCase("Shopping"))
			this.mMarker = CTX.getResources().getDrawable(R.drawable.marker5);
		else if (CategoryName.equalsIgnoreCase("Transportation"))
			this.mMarker = CTX.getResources().getDrawable(R.drawable.marker6);
		else if (CategoryName.equalsIgnoreCase("Imported"))
			this.mMarker = CTX.getResources().getDrawable(R.drawable.marker7);
		else
			this.mMarker = CTX.getResources().getDrawable(R.drawable.marker0);
	}

	protected void onDrawItem(final Canvas c, final int index, final Point curScreenCoords) {
		final int left = curScreenCoords.x - this.mMarkerHotSpot.x;
		final int right = left + this.mMarkerWidth;
		final int top = curScreenCoords.y - this.mMarkerHotSpot.y;
		final int bottom = top + this.mMarkerHeight;
		
		this.mMarker.setBounds(left, top, right, bottom);
		
		this.mMarker.draw(c);
	}
	
	@Override
	public boolean onSingleTapUp(final MotionEvent event, final OpenStreetMapView mapView) {
		final OpenStreetMapViewProjection pj = mapView.getProjection();
		final int eventX = (int)event.getX();
		final int eventY = (int)event.getY();
		
		final int markerWidth = this.mMarker.getIntrinsicWidth();
		final int markerHeight = this.mMarker.getIntrinsicHeight();
		
		/* These objects are created to avoid construct new ones every cycle. */
		final Rect curMarkerBounds = new Rect();
		final Point mCurScreenCoords = new Point();
		final int viewWidth_2 = mapView.getWidth() / 2;
		final int viewHeight_2 = mapView.getHeight() / 2;
		int offsetX, offsetY;
		
		for(int i = 0; i < this.mItemList.size(); i++){
			final T mItem = this.mItemList.get(i);
			if (mItem.mGeoPointList.size() == 1 ){ //skip path, works on points only
				pj.toPixels(mItem.mGeoPointList.get(0), mCurScreenCoords);
				if (mapView.getBearing() != 0){
					final double rotateAngle =  Math.toDegrees(Math.atan2(mCurScreenCoords.y - viewHeight_2, mCurScreenCoords.x - viewWidth_2))-mapView.getBearing();
					final double radius = Math.sqrt((Math.pow(Math.abs(mCurScreenCoords.x-viewWidth_2) , 2) + Math.pow(Math.abs(mCurScreenCoords.y-viewHeight_2), 2)));
					offsetX = (int) (Math.cos(Math.toRadians(rotateAngle)) * (radius)) + viewWidth_2;
					offsetY = (int) (Math.sin(Math.toRadians(rotateAngle)) * (radius)) + viewHeight_2;
				}
				else{
					offsetX = mCurScreenCoords.x;
					offsetY = mCurScreenCoords.y;
				}
				// only works on items in visible area
				if (offsetX >= 0 && offsetY >= 0){
					//final int left = mCurScreenCoords.x - this.mMarkerHotSpot.x;
					final int left = offsetX  - this.mMarkerHotSpot.x ;
					final int right = left + markerWidth ;
					//final int top = mCurScreenCoords.y - this.mMarkerHotSpot.y;
					final int top = offsetY  - this.mMarkerHotSpot.y;
					final int bottom = top + markerHeight;
	
					curMarkerBounds.set(left, top, right, bottom);
		//			new AlertDialog.Builder(CTX)
		//            .setMessage("le:"+Integer.toString(left) +
		//           		 " x:"+Integer.toString(eventX) +
		//        		 " ri:"+Integer.toString(right) +
		//					 " to:"+Integer.toString(top) +
		//        		 " y:"+Integer.toString(eventY) +
		//					 " bo:"+Integer.toString(bottom)+
		//					 " ang0:" + Double.toString(mapView.getBearing()) +
		//					 " ang1:" + Double.toString(Math.toDegrees(Math.atan2(mCurScreenCoords.y - viewHeight_2, mCurScreenCoords.x - viewWidth_2))) +
		//					 " ang2:" + Double.toString(mapView.getBearing()-Math.toDegrees(Math.atan2(mCurScreenCoords.y - viewHeight_2, mCurScreenCoords.x - viewWidth_2))) )
		//            .setMessage("ox:"+Integer.toString(offsetX) +
					
					if(curMarkerBounds.contains(eventX, eventY))
						if(onTap(i))
							return true;
				}
			}
		}
		return super.onSingleTapUp(event, mapView);
	}

	// ===========================================================
	// Methods
	// ===========================================================
	
	protected boolean onTap(int pIndex) {
		if(this.mOnItemTapListener != null)
			return this.mOnItemTapListener.onItemTap(pIndex, this.mItemList.get(pIndex));
		else
			return false;
	}

	//joeng
	public boolean onLongPress(final MotionEvent event, final OpenStreetMapView mapView) {
		final OpenStreetMapViewProjection pj = mapView.getProjection();
		final int eventX = (int)event.getX();
		final int eventY = (int)event.getY();
		
		final int markerWidth = this.mMarker.getIntrinsicWidth();
		final int markerHeight = this.mMarker.getIntrinsicHeight();
		
		/* These objects are created to avoid construct new ones every cycle. */
		final Rect curMarkerBounds = new Rect();
		final Point mCurScreenCoords = new Point();
		final int viewWidth_2 = mapView.getWidth() / 2;
		final int viewHeight_2 = mapView.getHeight() / 2;
		int offsetX, offsetY;
		
		for(int i = 0; i < this.mItemList.size(); i++){
			final T mItem = this.mItemList.get(i);
			if (mItem.mGeoPointList.size() == 1 ){ //skip path, works on points only
				pj.toPixels(mItem.mGeoPointList.get(0), mCurScreenCoords);

				if (mapView.getBearing() != 0){
					final double rotateAngle =  Math.toDegrees(Math.atan2(mCurScreenCoords.y - viewHeight_2, mCurScreenCoords.x - viewWidth_2))-mapView.getBearing();
					final double radius = Math.sqrt((Math.pow(Math.abs(mCurScreenCoords.x-viewWidth_2) , 2) + Math.pow(Math.abs(mCurScreenCoords.y-viewHeight_2), 2)));
					offsetX = (int) (Math.cos(Math.toRadians(rotateAngle)) * (radius)) + viewWidth_2;
					offsetY = (int) (Math.sin(Math.toRadians(rotateAngle)) * (radius)) + viewHeight_2;
				}
				else{
					offsetX = mCurScreenCoords.x;
					offsetY = mCurScreenCoords.y;
				}
				
				if (offsetX >= 0 && offsetY >= 0){			
					final int left = offsetX - this.mMarkerHotSpot.x;
					final int right = left + markerWidth;
					final int top = offsetY - this.mMarkerHotSpot.y;
					final int bottom = top + markerHeight;
					
					curMarkerBounds.set(left, top, right, bottom);
					if(curMarkerBounds.contains(eventX, eventY))
						if(onItemPress(i))
							return true;
				}
			}
		}
		return super.onLongPress(event, mapView);
	}
	
	protected boolean onItemPress(int pIndex){
		if(this.mOnItemLongPressListener != null)
			return this.mOnItemLongPressListener.onItemLongPress(pIndex, this.mItemList.get(pIndex));
		else
			return false;
	}

	// ===========================================================
	// Inner and Anonymous Classes
	// ===========================================================
	public static interface OnItemTapListener<T>{
		public boolean onItemTap(final int aIndex, final T aItem);
	}
	//joeng
	public static interface OnItemLongPressListener<T>{
		public boolean onItemLongPress(final int aIndex, final T aItem);
	}

}
